package ui;

import infrastructure.LoggingManager;
import infrastructure.Utils;

import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import performance.PerformanceResult;

public class TestingPerformance {

	private static Logger logger = LoggingManager.getLogger();
	private static final String PREFIX_PROTEIN_START = ">";
	private static final String FLAG_TO_START_READING_WEKA_FILE = "@DATA";	
	
	/** Perfoms the evaluation of the results. 
	 * @param args
	 */
	public static void main(String[] args) {
		// Parameter 0, is then path to the  weka file. This contains the ground true of the results.
		String fileNameGroundTrue = args[0];
		
		// Parameter 1, is the path to the file, where we have placed the results
		String fileNameTestResult = args[1];
				
		try {
			List<String> groundTrueWeka = Utils.readFile(fileNameGroundTrue, FLAG_TO_START_READING_WEKA_FILE);
			//System.out.println("Ground True Lines "+ groundTrueWeka.size());
			List<String> groundTrue = convertToTestFormat(groundTrueWeka);
			//System.out.println("Ground True Lines in new Format "+ groundTrue.size());
			List<String> testResults = Utils.readFile(fileNameTestResult, null);
			//System.out.println("Test Results Lines "+ testResults.size());
			
			// We should assume that the order of the Test Result file match the order of the Ground True File.
			PerformanceResult performanceResult = evaluateResults(groundTrue, testResults);
			//System.out.println("\n -------------  \n ");
			System.out.println("Q2 " + round(performanceResult.getOverallPerformance()*100,2) + "%" );
			System.out.println("Acc(TM) " + round(performanceResult.getPositiveAccuracy()*100,2) + "%" );
			System.out.println("Acc(non-TM) " + round(performanceResult.getNegativeAccuracy()*100,2) + "%" );
			System.out.println("Cov(TM) " + round(performanceResult.getPositiveCoverage()*100,2) + "%" );
			System.out.println("Cov(non-TM) " + round(performanceResult.getNegativeCoverage()*100,2) + "%" );
			
		} catch (Exception e) {
			logger.error(e);
		}
	}

	/**
	 * This method performs the evaluation of the results. Files should contain same format.
	 * @param groundTrue
	 * @param testResults
	 * @return The Performance Result of the test.
	 * @throws Exception 
	 */
	private static PerformanceResult evaluateResults(List<String> groundTrue,
			List<String> testResults) throws Exception {
		
		PerformanceResult performanceResult = new PerformanceResult();
		
		if (groundTrue.size() != testResults.size())
			throw new Exception("Ground True File does not match the Test Results File. Evaluation can not be performed");
		
		for (int i = 0; i < groundTrue.size(); i++) {
			String resultLine = testResults.get(i);
			String groundTrueLine = groundTrue.get(i);
			
			// Check that proteins that we are comparing are the same
			if (resultLine.startsWith(PREFIX_PROTEIN_START)){
				if (!resultLine.equals(groundTrueLine))
					throw new Exception("Proteins are not the same. Check the format of the file. Evaluation can not be performed.");
			} else {
				// Then compare line to line
				compareLineToLine(performanceResult, resultLine, groundTrueLine);
			}
		}
		
		return performanceResult;
	}

	/**
	 * Compares the result line vs the ground True Line, and then uses the performance result to evaluate each result.
	 * @param performanceResult The object keeping track of the result
	 * @param resultLine
	 * @param groundTrueLine
	 * @throws Exception 
	 */
	private static void compareLineToLine(PerformanceResult performanceResult, String resultLine,
			String groundTrueLine) throws Exception {
		if (resultLine.length() != groundTrueLine.length())
			throw new Exception("Results for the protein do not match in length.");
		for (int i =0; i < resultLine.length(); i++)
			performanceResult.evaluateResult(resultLine.charAt(i) , groundTrueLine.charAt(i));		
	}

	/**
	 * Creates a List of Strings with the same format of the Test Result.
	 * @param groundTrueWeka
	 * @return
	 */
	private static List<String> convertToTestFormat(List<String> groundTrueWeka) {
		List<String> testFormatResults = new ArrayList<String>();
		String newResultLine = null;
		String currentProteinName = null;
		for(String line : groundTrueWeka){
			String [] columns = line.split(",");
			String newProteinName = columns[0].split("_")[0];
			String newResult = columns[columns.length - 1];
						
			// First time, then currentProteinName is null
			if (currentProteinName == null){
				currentProteinName = newProteinName;
				testFormatResults.add(">"+currentProteinName);
				newResultLine = newResult;
			} else {
				// If names are equal, then concatenate the result
				if (currentProteinName.equals(newProteinName)){
					newResultLine += newResult;
				} else {
					// If a new protein, save the result line
					testFormatResults.add(newResultLine);
					// and then create a new Line
					currentProteinName = newProteinName;
					testFormatResults.add(">"+currentProteinName);
					newResultLine = newResult;					
				}
			}
		}
		
		// Adds the last REsult Line
		testFormatResults.add(newResultLine);
		return testFormatResults;
	}
	
	public static double round(double value, int places) {
	    if (places < 0) throw new IllegalArgumentException();

	    long factor = (long) Math.pow(10, places);
	    value = value * factor;
	    long tmp = Math.round(value);
	    return (double) tmp / factor;
	}
}
